package cn.lg.soar.system.auth.service.impl;

import cn.lg.soar.common.data.tree.ITreeData;
import cn.lg.soar.common.data.tree.ITreeResult;
import cn.lg.soar.common.data.tree.ITreeSource;
import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.common.util.current.CurrentProxy;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.database.base.service.impl.TreeServiceImpl;
import cn.lg.soar.system.api.enums.MenuStatusEnum;
import cn.lg.soar.system.api.enums.MenuTypeEnum;
import cn.lg.soar.system.auth.entity.Menu;
import cn.lg.soar.system.auth.entity.MenuPermit;
import cn.lg.soar.system.auth.entity.RoleMenu;
import cn.lg.soar.system.auth.mapper.MenuMapper;
import cn.lg.soar.system.auth.model.MenuTree;
import cn.lg.soar.system.auth.model.MenuVo;
import cn.lg.soar.system.auth.service.IMenuService;
import cn.lg.soar.system.auth.service.MenuPermitService;
import cn.lg.soar.system.auth.service.RoleMenuService;
import cn.lg.soar.system.config.AuthCacheKey;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author luguoxiang
 * @date 2021/8/15
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class MenuServiceImpl extends TreeServiceImpl<Long, MenuMapper, Menu> implements IMenuService, CurrentProxy<IMenuService> {

    @Autowired
    private RoleMenuService roleMenuService;
    @Autowired
    private MenuPermitService menuPermitService;

    @Caching(evict = {
            @CacheEvict(value = { AuthCacheKey.MENU_ID_BY_ROLE }, allEntries = true),
            @CacheEvict(value = AuthCacheKey.TREE, key = "'menu'")
    })
    @Override
    public void clearCache() {}

    @CacheEvict(value = AuthCacheKey.TREE, key = "'menu'")
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void create(MenuVo vo) {
        Menu menu = vo.toMenu();
        AssertUtil.isTrue(save(menu), "创建失败");
        menuPermitService.setMenuPermit(menu.getId(), vo.getPermitIds());
        // 添加菜单权限数据
        if (MenuTypeEnum.menu.eq(vo.getType())) {
            List<MenuVo> permits = vo.getPermits();
            for (MenuVo permit : permits) {
                permit.setParentId(menu.getId());
                permit.setType(MenuTypeEnum.button.getValue());
                if (permit.getStatus() == null) {
                    permit.setStatus(MenuStatusEnum.normal.getValue());
                }
                AssertUtil.isTrue(save(permit), "添加权限失败");
                menuPermitService.setMenuPermit(permit.getId(), permit.getPermitIds());
            }
        }
    }

    @CacheEvict(value = AuthCacheKey.TREE, key = "'menu'")
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void update(MenuVo vo) {
        Menu menu = vo.toMenu();
        AssertUtil.isTrue(updateById(menu), "更新失败");
        menuPermitService.setMenuPermit(menu.getId(), vo.getPermitIds());
        // 添加菜单权限数据
        if (MenuTypeEnum.menu.eq(vo.getType())) {
            List<MenuVo> permits = vo.getPermits();
            // 删除多余的权限
            List<Long> ids = permits.stream().map(Menu::getId).filter(Objects::nonNull).collect(Collectors.toList());
            remove(
                    Wrappers.<Menu>lambdaQuery()
                            .eq(Menu::getParentId, menu.getId())
                            .notIn(DataUtil.isValuable(ids), Menu::getId, ids)
            );
            // 添加新的权限
            for (MenuVo permit : permits) {
                permit.setParentId(menu.getId());
                permit.setType(MenuTypeEnum.button.getValue());
                if (permit.getStatus() == null) {
                    permit.setStatus(MenuStatusEnum.normal.getValue());
                }
                AssertUtil.isTrue(saveOrUpdate(permit), "添加权限失败");
                menuPermitService.setMenuPermit(permit.getId(), permit.getPermitIds());
            }
        }
    }

    @CacheEvict(value = AuthCacheKey.TREE, key = "'menu'")
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean removeByIds(Collection<?> list) {
        if (DataUtil.isEmpty(list)) {
            return true;
        }
        Set<?> set = new HashSet<>(list);
        List<MenuTree> tree = proxy().tree();
        Set<Long> ids = new HashSet<>(list.size());
        ITreeResult.forEach(tree, x -> {
            if (set.contains(x.getId())) {
                ids.add(x.getId());
                ITreeResult.forEach(x.getChildren(), y -> {
                    ids.add(y.getId());
                });
            }
        });

        if (!ids.isEmpty()) {
            roleMenuService.remove(Wrappers.<RoleMenu>lambdaQuery().in(RoleMenu::getMenuId, ids));
            menuPermitService.remove(Wrappers.<MenuPermit>lambdaQuery().in(MenuPermit::getMenuId, ids));
        }
        roleMenuService.remove(Wrappers.<RoleMenu>lambdaQuery().in(RoleMenu::getMenuId, list));
        menuPermitService.remove(Wrappers.<MenuPermit>lambdaQuery().in(MenuPermit::getMenuId, list));
        return super.removeByIds(list);
    }

    @Override
    public MenuVo detail(Long id) {
        Menu menu = getById(id);
        if (menu == null){
            return null;
        }
        MenuVo vo = new MenuVo(menu);
        // 接口权限
        List<Long> permitIds = menuPermitService.getPermitIds(id);
        vo.setPermitIds(permitIds);
        // 按钮列表
        if (MenuTypeEnum.menu.eq(menu.getType())) {
            List<MenuVo> children = getChildren(id)
                    .stream()
                    .map(MenuVo::new)
                    .collect(Collectors.toList());
            for (MenuVo menuVo : children) {
                menuVo.setPermitIds(menuPermitService.getPermitIds(menuVo.getId()));
            }
            vo.setPermits(children);
        }
        return vo;
    }

    @Cacheable(value = AuthCacheKey.TREE, key = "'menu'")
    @Override
    public List<MenuTree> tree() {
        List<Menu> menus = list(
                Wrappers.<Menu>lambdaQuery()
                        .select(
                                Menu::getId,
                                Menu::getCode,
                                Menu::getPath,
                                Menu::getRedirect,
                                Menu::getComponent,
                                Menu::getTitle,
                                Menu::getIcon,
                                Menu::getHideChildren,
                                Menu::getKeepalive,
                                Menu::getShared,
                                Menu::getParentId,
                                Menu::getType,
                                Menu::getStatus,
                                Menu::getSort
                        )
                        .in(Menu::getStatus, MenuStatusEnum.normal.getValue(), MenuStatusEnum.hidden.getValue())
                        .orderByAsc(Menu::getParentId, Menu::getSort)
        );
        return ITreeResult.valueOf(menus, 0L, MenuTree::new);
    }
}

